package com.example.demo2.bean;

import java.text.SimpleDateFormat;
import java.util.Date;

import com.example.demo2.define.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.ExtendedState;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.WithStateMachine;

import reactor.core.publisher.Mono;

/**
 * CD 播放器
 *
 * 通过各种操作方法（如 play(), stop(), load() 等）向状态机发送事件
 * 通过 @OnTransition 和 @StatesOnTransition 注解的方法接收状态转换通知并更新 LCD 状态显示
 *
 * 状态机组件标识: @WithStateMachine 注解用于标识 CdPlayer 类是一个与状态机相关的组件，
 * 表明该类中的方法将与 Spring StateMachine 进行交互。
 * 状态转换监听: 该注解使得类中的 @OnTransition 和 @StatesOnTransition 注解的方法能够被 Spring StateMachine 自动识别和注册为状态转换监听器。
 * 自动注册机制: 带有此注解的类会被自动注册到 Spring 容器中，并与配置的状态机实例进行绑定，无需手动配置监听器。
 * 事件处理能力: 使得 CdPlayer 类能够通过 stateMachine.sendEvent() 方法发送事件到状态机，并接收状态机的状态变化通知。
 */
@WithStateMachine
public class CdPlayer {
    @Autowired
    private StateMachine<CDStates, CDEvents> stateMachine;

    private String cdStatus = "No CD";
    private String trackStatus = "";

    public void load(Cd cd) {
        System.out.println("...Press player load button");
        System.out.println("...Loading cd " + cd);
        System.out.println("...cur state:" + getFSMStatus());
        System.out.println("...send Event: LOAD");
        stateMachine
                .sendEvent(Mono.just(MessageBuilder
                        .withPayload(CDEvents.LOAD)
                        .setHeader(Variables.CD.toString(), cd)
                        .build()))
                .subscribe();
        System.out.println("...Load cd After state:" + getFSMStatus());
    }

    public void play() {
        System.out.println("*** Press player play button");
        System.out.println("...send Event: PLAY");
        stateMachine
                .sendEvent(Mono.just(MessageBuilder
                        .withPayload(CDEvents.PLAY).build()))
                .subscribe();
        System.out.println("*** play button After state:" + getFSMStatus());
    }

    public void stop() {
        System.out.println("Press player stop button");
        stateMachine
                .sendEvent(Mono.just(MessageBuilder.withPayload(CDEvents.STOP).build()))
                .subscribe();
        System.out.println("stop button After state:" + getFSMStatus());
    }

    public void pause() {
        System.out.println("Press player pause button");
        stateMachine
                .sendEvent(Mono.just(MessageBuilder
                        .withPayload(CDEvents.PAUSE).build()))
                .subscribe();
        System.out.println("pause button After state:" + getFSMStatus());
    }

    /**
     * 弹出 CD
     */
    public void eject() {
        System.out.println("^^^ Press player eject button");
        System.out.println("^^^ send Event: EJECT");
        stateMachine
                .sendEvent(Mono.just(MessageBuilder
                        .withPayload(CDEvents.EJECT).build()))
                .subscribe();
        System.out.println("^^^ eject button After state:" + getFSMStatus());
    }

    public void forward() {
        System.out.println("Press player forward button");
        stateMachine
                .sendEvent(Mono.just(MessageBuilder
                        .withPayload(CDEvents.FORWARD)
                        .setHeader(Headers.TRACKSHIFT.toString(), 1).build()))
                .subscribe();
        System.out.println("forward button After state:" + getFSMStatus());
    }

    public void back() {
        System.out.println("Press player back button");
        stateMachine
                .sendEvent(Mono.just(MessageBuilder
                        .withPayload(CDEvents.BACK)
                        .setHeader(Headers.TRACKSHIFT.toString(), -1).build()))
                .subscribe();
        System.out.println("back button After state:" + getFSMStatus());
    }

    public String getLcdStatus() {
        return "CD当前状态:" + cdStatus + ",歌曲状态:" + trackStatus;
    }

    public String getFSMStatus() {
        if(null == stateMachine) return "No StateMachine";
        var state = stateMachine.getState();
        if(state == null) return "No State";
        return state.getId().toString();
    }

    @OnTransition(target = "BUSY")
    public void busy(ExtendedState extendedState) {
        Object cd = extendedState.getVariables().get(Variables.CD);
        if (cd != null) {
            cdStatus = ((Cd)cd).getName();
        }
    }


    @StatesOnTransition(target = CDStates.PLAYING)
    public void playing(ExtendedState extendedState) {
        Object elapsed = extendedState.getVariables().get(Variables.ELAPSEDTIME);
        Object cd = extendedState.getVariables().get(Variables.CD);
        Object track = extendedState.getVariables().get(Variables.TRACK);
        if (elapsed instanceof Long && track instanceof Integer && cd instanceof Cd) {
            SimpleDateFormat format = new SimpleDateFormat("mm:ss");
            trackStatus = ((Cd) cd).getTracks()[((Integer) track)]
                    + " " + format.format(new Date((Long) elapsed));
        }
    }

    @StatesOnTransition(target = CDStates.OPEN)
    public void open(ExtendedState extendedState) {
        cdStatus = "Open";
    }

    @StatesOnTransition(target = {CDStates.CLOSED, CDStates.IDLE})
    public void closed(ExtendedState extendedState) {
        Object cd = extendedState.getVariables().get(Variables.CD);
        if (cd != null) {
            cdStatus = ((Cd)cd).getName();
        } else {
            cdStatus = "No CD";
        }
        trackStatus = "";
    }

}

